home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue30 / expreval / EXPREVAL.ZIP / Expressions.pas next >
Encoding:
Pascal/Delphi Source File  |  1998-01-02  |  61.4 KB  |  2,136 lines

  1. unit Expressions;
  2. interface
  3. {main documentation block just before implementation.
  4.  
  5. This unit written by Martin Lafferty of Production Robots Engineering Ltd
  6. 18/6/97
  7.  
  8. If you have any questions/comments I would be pleased to hear from you.
  9.  
  10. If you discover any bugs in this software I would be VERY pleased to
  11. hear from you.
  12.  
  13. If you want to offer me work implementing extensions or applications using this
  14. software then I will be moderately pleased to hear from you (if I am busy) or
  15. VERY VERY pleased to hear from you (if I am not)
  16.  
  17. in any event, my address is:   robots@enterprise.net
  18.  
  19. I have found this code very useful and surprisingly robust. I sincerely hope you do too.
  20.  
  21.  
  22. This code developed with Delphi 3.0, but I can't offhand think of any reason why it wouldn't
  23. work with Delphi 2.0. I have a 16 bit (Delphi 1.0) version somewhere: email me if you are
  24. interested in that and I will dig it out. It is not well documented though.
  25. }
  26.  
  27. uses
  28.   Classes,
  29.   SysUtils;
  30.  
  31. type
  32.   TExprType = (ttString, ttFloat, ttInteger, ttBoolean);
  33.  
  34.   TExpression =
  35.   class
  36.   private
  37.   protected
  38.     function GetAsString: String; virtual;
  39.     function GetAsFloat: Double; virtual;
  40.     function GetAsInteger: Integer; virtual;
  41.     function GetAsBoolean: Boolean; virtual;
  42.     function GetExprType: TExprType; virtual; abstract;
  43.   public
  44.     property AsString: String read GetAsString;
  45.     property AsFloat: Double read GetAsFloat;
  46.     property AsInteger: Integer read GetAsInteger;
  47.     property AsBoolean: Boolean read GetAsBoolean;
  48.     property ExprType: TExprType read GetExprType;
  49.     function CanReadAs(aExprType: TExprType): Boolean;
  50.       {means 'can be interpreted as'. Sort of}
  51.     constructor Create;
  52.     destructor Destroy; override;
  53.   end;
  54.  
  55.   TStringLiteral =
  56.   class(TExpression)
  57.   private
  58.     FAsString: String;
  59.   protected
  60.     function GetAsString: String; override;
  61.     function GetExprType: TExprType; override;
  62.   public
  63.     constructor Create( aAsString: String);
  64.   end;
  65.  
  66.   TFloatLiteral =
  67.   class(TExpression)
  68.   private
  69.     FAsFloat: Double;
  70.   protected
  71.     function GetAsString: String; override;
  72.     function GetAsFloat: Double; override;
  73.     function GetExprType: TExprType; override;
  74.   public
  75.     constructor Create( aAsFloat: Double);
  76.   end;
  77.  
  78.   TIntegerLiteral =
  79.   class(TExpression)
  80.   private
  81.     FAsInteger: Integer;
  82.   protected
  83.     function GetAsString: String; override;
  84.     function GetAsFloat: Double; override;
  85.     function GetAsInteger: Integer; override;
  86.     function GetExprType: TExprType; override;
  87.   public
  88.     constructor Create( aAsInteger: Integer);
  89.   end;
  90.  
  91.   TBooleanLiteral =
  92.   class(TExpression)
  93.   private
  94.     FAsBoolean: Boolean;
  95.   protected
  96.     function GetAsString: String; override;
  97.     function GetAsFloat: Double; override;
  98.     function GetAsInteger: Integer; override;
  99.     function GetAsBoolean: Boolean; override;
  100.     function GetExprType: TExprType; override;
  101.   public
  102.     constructor Create( aAsBoolean: Boolean);
  103.   end;
  104.  
  105.   TParameterList =
  106.   class(TList)
  107.   private
  108.     function GetAsString(i: Integer): String;
  109.     function GetAsFloat(i: Integer): Double;
  110.     function GetAsInteger(i: Integer): Integer;
  111.     function GetAsBoolean(i: Integer): Boolean;
  112.     function GetExprType(i: Integer): TExprType;
  113.     function GetParam(i: Integer): TExpression;
  114.   public
  115.     destructor Destroy; override;
  116.     property Param[i: Integer]: TExpression read GetParam;
  117.     property ExprType[i: Integer]: TExprType read GetExprType;
  118.     property AsString[i: Integer]: String read GetAsString;
  119.     property AsFloat[i: Integer]: Double read GetAsFloat;
  120.     property AsInteger[i: Integer]: Integer read GetAsInteger;
  121.     property AsBoolean[i: Integer]: Boolean read GetAsBoolean;
  122.   end;
  123.  
  124.   TFunction =
  125.   class(TExpression)
  126.   private
  127.     FParameterList: TParameterList;
  128.     function GetParam(n: Integer): TExpression;
  129.   public
  130.     constructor Create( aParameterList: TParameterList);
  131.     destructor Destroy; override;
  132.     function ParameterCount: Integer;
  133.     property Param[n: Integer]: TExpression read GetParam;
  134.   end;
  135.  
  136.   EExpression = class(Exception);
  137.  
  138.   TIdentifierFunction = function( const Identifier: String;
  139.                                   ParameterList: TParameterList): TExpression of Object;
  140.  
  141. function CreateExpression( const S: String;
  142.                 IdentifierFunction: TIdentifierFunction): TExpression;
  143. const
  144.   MaxStringLength = 255; {why?}
  145.  
  146.   {to get a string representation of TExprType use NExprType[ExprType] }
  147.   NExprType: array[TExprType] of String =
  148.       ('String', 'Float', 'Integer', 'Boolean');
  149.  
  150.  
  151.  
  152. {for debugging version, checking memory leaks}
  153. var
  154.   InstanceCount: Integer = 0;
  155.  
  156. {This unit comprises a mixed type expression evaluator which follows pascal
  157. syntax (reasonably accurately) and approximates standard pascal types.
  158.  
  159. Delphi already implements more than one mechanism for providing type flexibility - variants
  160. are the most notable of these. It may have been rational to implement this unit using Variants,
  161. but I have chosen not to do so. Instead, I use an a approach which is (vaguely) analogous to
  162. the approach used by TField and its descendents.
  163.  
  164. The Basics
  165. ----------
  166. The class defined above, TExpression, represents an expression in the most general sense. It
  167. has a type and a Value. The key properties of TExpression are:
  168.  
  169.     property AsString: String;
  170.     property AsFloat: Double;
  171.     property AsInteger: Integer;
  172.     property AsBoolean: Boolean;
  173.     property ExprType: TExprType;
  174.  
  175. note
  176.     TExprType = (ttString, ttFloat, ttInteger, ttBoolean);
  177.  
  178. Not all of these properties will yield a valid result, but more than one of them might. This
  179. sounds a little confusing, but isn't. Consider an expression in which ExprType = ttString and
  180. AsString = 'This is a string'. In this case, AsFloat, AsInteger, AsBoolean are all invalid, and
  181. any attempt to reference them will raise an exception (of type EExpression). On the other hand,
  182. an expression where ExprType = ttBoolean and AsBoolean = TRUE, will yield valid results for all
  183. of AsInteger (1), AsFloat (1.0), AsString ('TRUE'). These are called (my nomenclature) 'implicit
  184. upcasts'. By comparison to Pascal, Type checking is quite liberal and these 'upcasts' will always
  185. be made if required by the syntax of the expression. The four supported types are graded
  186. according to their 'generality'. By this reckoning ttString > ttFloat > ttInteger > ttBoolean.
  187. A more 'specific' type will always be cast to a more 'general' type if necessary. Typecasts
  188. can also be forced using Pascal Syntax (e.g. String(10) = '10'), but are seldom necessary.
  189.  
  190. In contrast 'downcasts' ie from a general type to a more specific will never be made implicitly
  191. and cannot be forced. For example, a ttString can never be cast into ttFloat even if the string
  192. forms a valid floating number, say '3.142'. Support for downcasts could be added to this unit
  193. and may be useful, but for now these casts will fail.
  194.  
  195.  
  196. The basic mechanism for creating expressions is the function
  197.  
  198. function CreateExpression( const S: String;
  199.                 IdentifierFunction: TIdentifierFunction): TExpression;
  200.  
  201. Perhaps unsurprisingly, CreateExpression creates an object of type TExpression (or a descendent).
  202. The properties of this object give the value of the expression.
  203.  
  204. Parameters
  205.   S: String
  206.   This constains the string you wish to parse. This string is the expression you wish to
  207.   evaluate, as you would enter it into your code or into the 'Evalute/Modify' dialog box.
  208.  
  209.   arbitrary examples:
  210.  
  211.   4*5 + 2
  212.   (pi/3 + 2.5) < 5.78
  213.   410 div Pos('st', 'this is a string')
  214.  
  215.   IdentifierFunction:
  216.   This is a function which you may provide if you wish to support additional indentifiers
  217.   in addition to the standard functions and operators. If your expressions contain only
  218.   literals, operators and standard functions, you may pass NIL as an identifier function.
  219.  
  220. If you create an expression using CreateExpression you must remember to dispose of it. Use
  221. TExpression.Free for this purpose.
  222.  
  223. Example 1
  224. ---------
  225.  
  226. procedure TForm1.EG1ButtonClick(Sender: TObject);
  227. var
  228.   s: String;
  229.   E: TExpression;
  230. begin
  231.   s:= '';
  232.   if InputQuery('Tester', 'Enter an expression...', s) then
  233.   begin
  234.     E:= CreateExpression(s, nil);
  235.     if Assigned(E) then
  236.     try
  237.       MessageDlg(
  238.         Format('E.AsString = %s E.ExprType = %s',
  239.                [E.AsString, NExprType[E.ExprType]]),
  240.         mtInformation, [mbOK], 0)
  241.     finally
  242.       E.Free
  243.     end
  244.   end
  245. end;
  246.  
  247. This code is implemented in the 'Tester' project distributed with this file, and attached to
  248. TestForm.EG1Button.
  249.  
  250.  
  251. Standard Operators
  252. ------------------
  253. The best reference source for the operators / functions supported by this
  254. unit is the Borland Pascal 7.0 Language Guide - Ch6 'Expressions'. This is
  255. possibly the most recent piece of clear, comprehensive documentation issued
  256. by Borland. What follows is summarised from this excellent source material.
  257.  
  258. The following operators are supported:
  259.  
  260. Binary Arithmetic Operators
  261. Operator   Operation          Operand Types         Result Type
  262. --------   ---------          -------------         -----------
  263. +          addition           Integer               Integer
  264.                               Float                 Float
  265. -          subtraction        Integer               Integer
  266.                               Float                 Float
  267. *          multiplication     Integer               Integer
  268.                               Float                 Float
  269. /          division           Integer               Float
  270.                               Float                 Float
  271. div        integer division   Integer               Integer
  272. mod        modulo             Integer               Integer
  273.  
  274. Unary Arithmetic Operators
  275. Operator   Operation          Operand Type          Result Type
  276. --------   ---------          ------------          -----------
  277. +          sign identity      Integer               Integer
  278.                               Float                 Float
  279. -          sign negation      Integer               Integer
  280.                               Float                 Float
  281.  
  282. Logical (BITWISE) Operators
  283. (NB table 6.4 of BP Language guide contains errors)
  284. Operator   Operation          Operand Type(s)       Result Type
  285. --------   ---------          ---------------       -----------
  286. not        bitwise negation   Integer               Integer
  287. and        bitwise and        Integer               Integer
  288. or         bitwise or         Integer               Integer
  289. xor        bitwise xor        Integer               Integer
  290. shl        shift left         Integer               Integer
  291. shr        shift right        Integer               Integer
  292.  
  293. Boolean Operators
  294. Operator   Operation          Operand Type(s)       Result Type
  295. --------   ---------          ---------------       -----------
  296. not        negation           Boolean               Boolean
  297. and        logical and        Boolean               Boolean
  298. or         logical or         Boolean               Boolean
  299. xor        logical xor        Boolean               Boolean
  300.  
  301. String Operator
  302. Operator   Operation          Operand Types         Result Type
  303. --------   ---------          -------------         -----------
  304. +          concatenation      String                String
  305.  
  306. Relational Operators
  307. Operator Type  Operation      Operand Types         Result Type
  308. -------------  ---------      -------------         -----------
  309. =              equal          Compatible pair       Boolean
  310. <>             not equal      Compatible pair       Boolean
  311. <              less than      Compatible pair       Boolean
  312. >              greater than   Compatible pair       Boolean
  313. <=             less than or   Compatible pair       Boolean
  314.                equal to
  315. >=             greater than   Compatible pair       Boolean
  316.                or equal to
  317.  
  318. Note that this unit does not support Set Types or their operators
  319.  
  320. Standard Functions
  321. ------------------
  322. The following Standard functions are supported. For a full description of
  323. these functions and their parameters refer to Delphi or BP on-line help.
  324.  
  325. Arithmetic functions
  326.  TRUNC, ROUND, ABS, ARCTAN, COS, EXP, FRAC, INT, LN, PI, SIN, SQR, SQRT
  327.  
  328.  
  329. String Functions
  330.   UPPER, LOWER, COPY, POS, LENGTH
  331.  
  332. in addition to the RTL functions, the following specials are supported:
  333.  
  334.  function Power(Base, Exponent: Float): Float;
  335.  
  336. This unit also supports a form of the 'C' construct known as 'Conditional Expression',
  337. also found in many spreadsheets. This is the IF expression
  338.  
  339. IF(Condition, TrueResult, FalseResult)
  340.  
  341. Condition is a Boolean expression. When the function is evaluated,
  342. it returns TrueResult if Condition else FalseResult. TrueResult and FalseResult
  343. need not be of the same type and the type of the IF expression may change depending
  344. on Condition. For this reason IF cannot be considered a standard Pascal Function.
  345.  
  346.  
  347. Identifier Functions
  348. --------------------
  349. The capability of the parser to recognise artibrary identifiers may be extended
  350. by the use to an Identifier function. This is a powerful mechanism, but neither
  351. simple not particularly intuitive.
  352.  
  353. In order to really make the most of the identifier function you need to know more
  354. about how the parser works. When the parser comes across a token which is does not
  355. recognise as a literal, operator, or standard function, it calls the identifier
  356. function. It is important to remember that the identifier function is only called when
  357. the input string, S, is parsed i.e. the result of a call to CreateExpression is being
  358. constructed. Once GetExpression has returned the identifier function is not called.
  359. In particular the identifier function is not called when an expression is evaluated.
  360.  
  361. This distinction between the time when an expression is created (let's call it 'parse-time')
  362. and when it is evaluated ('evaluate-time') is important, particularly if the result of a
  363. call to CreateExpression is saved for future evaluation rather than simply evaluated then
  364. thrown away.
  365.  
  366. This difference between 'parse-time' and 'evaluate-time' is important when considering
  367. how to handle non-standard identifiers.
  368.  
  369. 'Constant' identifiers
  370. ----------------------
  371. It may be that you wish to substitute a constant expression for an identifier when
  372. a given string is parsed, and do not expect the value of the resultant expression to change
  373. during the lifetime of the result (of the call to CreateExpression). This lifetime might
  374. be really short, as in Example1 above or it might be the lifetime of your program.
  375. This sort of constant substitution is quite easy to do.
  376.  
  377. Consider the following example of an identifier function.
  378.  
  379. Example 2. Constant Substitution
  380. --------------------------------
  381.  
  382. function TForm1.EG2IDFunc( const Identifier: String;
  383.                               ParameterList: TParameterList): TExpression;
  384. begin
  385.   if Assigned(ParameterList) then
  386.     raise EExpression.CreateFmt('Identifier %s does not require parameters', [Identifier]);
  387.   if Identifier = 'SC' then
  388.     Result:= TStringLiteral.Create('This is a string')
  389.   else
  390.   if Identifier = 'FC' then
  391.     Result:= TFloatLiteral.Create(8.9)
  392.   else
  393.   if Identifier = 'IC' then
  394.     Result:= TIntegerLiteral.Create(42)
  395.   else
  396.   if Identifier = 'BC' then
  397.     Result:= TBooleanLiteral.Create(False)
  398.   else
  399.     Result:= nil
  400. end;
  401.  
  402. procedure TForm1.EG2ButtonClick(Sender: TObject);
  403. var
  404.   s: String;
  405.   E: TExpression;
  406. begin
  407.   s:= '';
  408.   if InputQuery('Example 2', 'Expression may contain' +
  409.      ' SC, FC, IC or BC', s) then
  410.   begin
  411.     E:= CreateExpression(s, EG2IDFunc);
  412.     if Assigned(E) then
  413.     try
  414.       MessageDlg(
  415.         Format('E.AsString = %s E.ExprType = %s',
  416.                [E.AsString, NExprType[E.ExprType]]),
  417.         mtInformation, [mbOK], 0)
  418.     finally
  419.       E.Free
  420.     end
  421.   end
  422. end;
  423.  
  424. Note that the IdentifierFunction (in this case EG2IDFunc) is implemented and then its address
  425. is passed to CreateExpression. The identifier function is called whenever the parser
  426. (CreateExpression) encounters an unknown identifier. Note that the additional identifiers do
  427. not require parameters, so if a parameter list is passed to the EG2IDFunc, an exception is
  428. raised. This step is important as if a parameter list is passed, and the the identifier list
  429. returns non-nil, then disposal of this list is the responsibility of the Identifier function
  430. or the expression it creates. See note on 'Supporting Parameters' below.
  431.  
  432. It is most important to appreciate that the string passed to the Identifier function as
  433. Identifier is always in UPPER CASE regardless of its case in the expression. As in Pascal
  434. identifiers are case insensitive in expressions.
  435.  
  436.  
  437. The expression returned by the IdentifierFunction must be Created by that call. Unless you
  438. are very sure what you are doing, you should not return a pointer to an expression already
  439. instantiated. This is because Expressions are more often than not, binary trees, containing
  440. pointers to many sub expressions. When an expression is freed, all its branches are freed
  441. also, including any which may have been created by a call to an IdentifierFunction.
  442.  
  443. Note that there is no reason why the IdentifierFunction cannot call CreateExpression to obtain
  444. a result. An Alternative implementation of the Identifier function above might be:
  445.  
  446. function TForm1.EG2IDFunc( const Identifier: String;
  447.                               ParameterList: TParameterList): TExpression;
  448. begin
  449.   if Assigned(ParameterList) then
  450.     raise EExpression.CreateFmt('Identifier %s does not require parameters', [Identifier]);
  451.   if Identifier = 'SC' then
  452.     Result:= CreateExpression('''This is a string''', nil)
  453.   else
  454.   if Identifier = 'FC' then
  455.     Result:= CreateExpression('8.9', nil)
  456.   else
  457.   if Identifier = 'IC' then
  458.     Result:= CreateExpression(42, nil)
  459.   else
  460.   if Identifier = 'BC' then
  461.     Result:= CreateExpression('False', nil)
  462.   else
  463.     Result:= nil
  464. end;
  465.  
  466. If an identifier expression calls CreateExpression it can pass itself as an identifier
  467. expression. If, however, the implementation of a particular identifier depends on that same
  468. identifier you will (obviously) get an infinite recursive loop. (Aside: Win95 can readjust
  469. its own stack and an infinite recusive loop will swallow up to a 1MB of stack before
  470. raising an exception. This can take some time, and it is possible to be misled into thinking
  471. that your program has hung when it fact it is simply busy consuming stack)
  472.  
  473. Example 3 Getting a value at 'evaluate-time'
  474. --------------------------------------------
  475. There may be circumstances when you want a particular identifier to represent a value that
  476. may change during the lifetime of your program, and you cannot afford the computational
  477. overhead of creating a new expression (by parsing a string) each time you want to evaluate
  478. the expression. In order to do this, you need to derive a descendent of TExpression. In
  479. the following simple example we define TTimeString = class(TExpression). This Expression
  480. has type ttString and returns the current time as a string in the format hh:mm:ss.
  481. The Identifier function EG3IDFunc returns an Expression of type TTimeString when passed
  482. an identifier of 'TIMESTRING' (NOTE: not 'TimeString'!)
  483.  
  484. When we click EG3Button we are prompted to enter an expression which is parsed and assigned
  485. to the variable EG3Expr (a field of TForm1). EG3Timer executes EG3TimerTimer every 5 seconds
  486. which evaluates the value of EG3Expr.AsString and assigns it to EG3Result.Caption. The
  487. important thing to note is that although EG3Expr is created only when EG3Button is clicked, if
  488. the string from which EG3Expr is derived contains the token TimeString then each time
  489. EG3Expr.AsString is evaluated, its value is different, depending on the system time.
  490.  
  491. Try entering an expression like
  492.  
  493. 'The time is now ' + TimeString
  494.  
  495. for example 3.
  496.  
  497. type
  498.   TTimeString =
  499.   class(TExpression)
  500.   protected
  501.     function GetAsString: String; override;
  502.     function GetExprType: TExprType; override;
  503.   end;
  504.  
  505. function TTimeString.GetAsString: String;
  506. begin
  507.   Result:= FormatDateTime('hh:mm:ss', SysUtils.Time)
  508. end;
  509.  
  510. function TTimeString.GetExprType: TExprType;
  511. begin
  512.   Result:= ttString
  513. end;
  514.  
  515. function TForm1.EG3IDFunc( const Identifier: String;
  516.                               ParameterList: TParameterList): TExpression;
  517. begin
  518.   if Assigned(ParameterList) then
  519.     raise EExpression.CreateFmt('Identifier %s does not require parameters', [Identifier]);
  520.   if Identifier = 'TIMESTRING' then
  521.     Result:= TTimeString.Create
  522.   else
  523.     Result:= nil
  524. end;
  525.  
  526. procedure TForm1.EG3TimerTimer(Sender: TObject);
  527. begin
  528.   if Assigned(EG3Expr) then
  529.     EG3Result.Caption:= EG3Expr.AsString
  530.   else
  531.     EG3Result.Caption:= 'EG3 not running'
  532. end;
  533.  
  534. procedure TForm1.FormDestroy(Sender: TObject);
  535. begin
  536.   EG3Expr.Free
  537. end;
  538.  
  539.  
  540. procedure TForm1.EG3ButtonClick(Sender: TObject);
  541. var
  542.   s: String;
  543. begin
  544.   s:= 'TimeString';
  545.   if InputQuery('Example 3', 'Expression may contain' +
  546.      ' TimeString', s) then
  547.   begin
  548.     EG3Expr.Free;
  549.     EG3Expr:= CreateExpression(s, EG3IDFunc);
  550.     CheckInstances
  551.   end
  552. end;
  553.  
  554.  
  555. Using Parameter Lists
  556. ---------------------
  557. It may be that you wish to define a custom function which has an arbitrary number and type
  558. of parameters. In your expression this might look like
  559.  
  560.    MyFunc(10*2 + 3, 'A String', True)
  561.  
  562. Upon encountering a construct like this, CreateExpression will construct a parameter list
  563. before calling the Identifier Function. A Parameter list is not an Expression, it is a
  564. descendent of TList which contains expressions, one for each parameter. The parameter list
  565. disposes of its expressions when it is freed.
  566.  
  567. If the identifier function returns nil, or raises an exception then CreateExpression will
  568. free the parameter list. Otherwise, it is assumed that the parameter list becomes the
  569. responsibility of the Identifier Function or its result. The issue of disposal of Parameter
  570. lists is complicated and poorly implemented by this unit (fair cop, guv'nor) There is
  571. further discussion on this topic in the 'General Notes' at the end of this file.
  572.  
  573. The Identifer function may elect to examine the parameter list, and then discard it, ignore it
  574. altogther, or save it to refer to later (at 'evaluate-time'). If the parameter list is
  575. required later, then it can be saved as a field of the Expression which is returned by the
  576. Identifier Function. Generally, this will be a descendent of TFunction (above). TFunction is
  577. an abstract - it differs from TExpression in that it has inbuilt mechanisms for handling
  578. parameters: you can pass in a parameter list when you construct it and the list will be freed
  579. by TFunction.Destroy.
  580.  
  581. Example 4 - Parameters
  582. ----------------------
  583. The following example uses parameters to construct a constant expression within an Identifier
  584. function. The identifier function disposes of the parameter list: the example uses no
  585. descendent of TFunction. To see how to implement a descendent of TFunction and
  586. refer to a parameter list at 'evaluate-time' examine the implementations of the standard
  587. functions.
  588.  
  589. We want CreateExpression to recognise the following function
  590.  
  591. function Mean(a, b: Float): Float;
  592. begin
  593.   Result:= (a + b)/2
  594. end;
  595.  
  596.  
  597. function TForm1.EG4IDFunc( const Identifier: String;
  598.                               ParameterList: TParameterList): TExpression;
  599. begin
  600.   if Identifier = 'MEAN' then
  601.   begin
  602.     if Assigned(ParameterList) and
  603.        (ParameterList.Count = 2) then
  604.     begin
  605.       with ParameterList do
  606.         Result:= TFloatLiteral.Create((AsFloat[0] + AsFloat[1])/2);
  607.       ParameterList.Free
  608.     end else
  609.     begin
  610.       raise EExpression.CreateFmt('Invalid Parameters to %s', [Identifier]);
  611.     end;
  612.   end else
  613.   begin
  614.     Result:= nil
  615.   end
  616. end;
  617.  
  618. The interesting things to note about EG4IDFunc are the following:
  619. the parameters are evaluated at 'parse-time' i.e before CreateExpression returns. The results
  620. are then used to create a literal float expression to return. The value of this expression
  621. will not now change within the lifetime of its 'parent' expression. Because the EG4IDFunc does
  622. not return a descendent of TFunction, and does not refer to its Parameter list at
  623. 'evaluate-time' it needs to dispose of its parameter list before it returns. This task would
  624. 'normally' be handled by TFunction.Destroy if EG4IDFunc suceeds. If EG4IDFunc fails
  625. (raises an exception or returns nil) then this duty is carried out by the caller, generally
  626. CreateExpression.
  627.  
  628. procedure TForm1.EG4ButtonClick(Sender: TObject);
  629. var
  630.   s: String;
  631.   E: TExpression;
  632. begin
  633.   s:= '';
  634.   if InputQuery('Example 4', 'Expression may contain ' +
  635.      'Mean(a, b: Float)', s) then
  636.   begin
  637.     E:= CreateExpression(s, EG4IDFunc);
  638.     if Assigned(E) then
  639.     try
  640.       MessageDlg(
  641.         Format('E.AsString = %s E.ExprType = %s',
  642.                [E.AsString, NExprType[E.ExprType]]),
  643.         mtInformation, [mbOK], 0)
  644.     finally
  645.       E.Free
  646.     end
  647.   end
  648. end;
  649.  
  650.  
  651. Other Ideas
  652. -----------
  653.  
  654. There is a lot of scope for extending the parser/evaluator presented here. I have already
  655. implemented a version which handles Date and Time computations. An Identifier function
  656. might be written to recognise field names and extract values from a database table at
  657. evaluate time. These sorts of things are not difficult.
  658.  
  659. I have written spreadsheet-like applications using this unit and it might be useful to
  660. implement more Spreadsheet type functions as standard.
  661. }
  662.  
  663.  
  664. implementation
  665. type
  666.   TOperator = ( opNot,
  667.                 opMult, opDivide, opDiv, opMod, opAnd, opShl, opShr,
  668.                 opPlus, opMinus, opOr, opXor,
  669.                 opEq, opNEQ, opLT, opGT, opLTE, opGTE);
  670.   TUnaryOp =
  671.   class(TExpression)
  672.   private
  673.     Operand: TExpression;
  674.     OperandType: TExprType;
  675.     Operator: TOperator;
  676.   protected
  677.     function GetAsFloat: Double; override;
  678.     function GetAsInteger: Integer; override;
  679.     function GetAsBoolean: Boolean; override;
  680.     function GetExprType: TExprType; override;
  681.   public
  682.     constructor Create( aOperator: TOperator; aOperand: TExpression);
  683.     destructor Destroy; override;
  684.   end;
  685.  
  686.   TBinaryOp =
  687.   class(TExpression)
  688.   private
  689.     Operand1, Operand2: TExpression;
  690.     Operator: TOperator;
  691.     OperandType: TExprType;
  692.   protected
  693.     function GetAsString: String; override;
  694.     function GetAsFloat: Double; override;
  695.     function GetAsInteger: Integer; override;
  696.     function GetAsBoolean: Boolean; override;
  697.     function GetExprType: TExprType; override;
  698.   public
  699.     constructor Create( aOperator: TOperator; aOperand1, aOperand2: TExpression);
  700.     destructor Destroy; override;
  701.   end;
  702.  
  703.   TRelationalOp =
  704.   class(TExpression)
  705.   private
  706.     Operand1, Operand2: TExpression;
  707.     Operator: TOperator;
  708.   protected
  709.     function GetAsString: String; override;
  710.     function GetAsFloat: Double; override;
  711.     function GetAsInteger: Integer; override;
  712.     function GetAsBoolean: Boolean; override;
  713.     function GetExprType: TExprType; override;
  714.   public
  715.     constructor Create( aOperator: TOperator; aOperand1, aOperand2: TExpression);
  716.     destructor Destroy; override;
  717.   end;
  718.  
  719. const
  720.   NOperator: array[TOperator] of String =
  721.               ( 'opNot',
  722.                 'opMult', 'opDivide', 'opDiv', 'opMod', 'opAnd', 'opShl', 'opShr',
  723.                 'opPlus', 'opMinus', 'opOr', 'opXor',
  724.                 'opEq', 'opNEQ', 'opLT', 'opGT', 'opLTE', 'opGTE');
  725.  
  726.   UnaryOperators = [opNot];
  727.   MultiplyingOperators = [opMult, opDivide, opDiv, opMod, opAnd, opShl, opShr];
  728.   AddingOperators = [opPlus, opMinus, opOr, opXor];
  729.   RelationalOperators = [opEQ, opNEQ, opLT, opGT, opLTE, opGTE];
  730.  
  731.   NBoolean: array[Boolean] of String[5] = ('FALSE', 'TRUE');
  732.  
  733.  
  734. function ResultType( Operator: TOperator; OperandType: TExprType): TExprType;
  735. procedure NotAppropriate;
  736. begin
  737.   Result:= ttString;
  738.   raise EExpression.CreateFmt( 'Operator %s incompatible with %s',
  739.                                [NOperator[Operator], NExprType[OperandType]])
  740. end;
  741.  
  742. begin
  743.   case OperandType of
  744.     ttString:
  745.     case Operator of
  746.       opPlus: Result:= ttString;
  747.       opEq..opGTE: Result:= ttBoolean;
  748.     else
  749.       NotAppropriate;
  750.     end;
  751.     ttFloat:
  752.     case Operator of
  753.       opMult, opDivide, opPlus, opMinus: Result:= ttFloat;
  754.       opEq..opGTE: Result:= ttBoolean;
  755.     else
  756.       NotAppropriate;
  757.     end;
  758.     ttInteger:
  759.     case Operator of
  760.       opNot, opMult, opDiv, opMod, opAnd, opShl, opShr, opPlus, opMinus,
  761.       opOr, opXor: Result:= ttInteger;
  762.       opDivide: Result:= ttFloat;
  763.       opEq..opGTE: Result:= ttBoolean;
  764.     else
  765.       NotAppropriate;
  766.     end;
  767.     ttBoolean:
  768.     case Operator of
  769.       opNot, opAnd, opOr, opXor, opEq, opNEQ: Result:= ttBoolean;
  770.     else
  771.       NotAppropriate;
  772.     end;
  773.   end
  774. end;
  775.  
  776. function CommonType( Op1Type, Op2Type: TExprType): TExprType;
  777. begin
  778.   if Op1Type < Op2Type then
  779.     Result:= Op1Type else
  780.     Result:= Op2Type
  781. end;
  782.  
  783. procedure Internal( Code: Integer);
  784. begin
  785.   raise EExpression.CreateFmt('Internal parser error. Code %d', [Code])
  786. end;
  787.  
  788. constructor TExpression.Create;
  789. begin
  790.   inherited Create;
  791.   Inc(InstanceCount)
  792. end;
  793.  
  794. destructor TExpression.Destroy;
  795. begin
  796.   Dec(InstanceCount);
  797.   inherited Destroy
  798. end;
  799.  
  800.  
  801. function TExpression.GetAsString: String;
  802. begin
  803.   case ExprType of
  804.     ttString: raise EExpression.CreateFmt('Cannot read %s as String',
  805.                                               [NExprType[ExprType]]);
  806.     ttFloat: Result:= FloatToStr(AsFloat);
  807.     ttInteger: Result:= IntToStr(AsInteger);
  808.     ttBoolean: Result:= NBoolean[AsBoolean];
  809.   end
  810. end;
  811.  
  812. function TExpression.GetAsFloat: Double;
  813. begin
  814.   Result:= 0;
  815.   case ExprType of
  816.     ttString, ttFloat:
  817.       raise EExpression.CreateFmt('Cannot read %s as Float',
  818.                                    [NExprType[ExprType]]);
  819.     ttInteger, ttBoolean: Result:= AsInteger;
  820.   end
  821. end;
  822.  
  823. function TExpression.GetAsInteger: Integer;
  824. begin
  825.   Result:= 0;
  826.   case ExprType of
  827.     ttString, ttFloat, ttInteger:
  828.        raise EExpression.CreateFmt('Cannot read %s as integer',
  829.                                [NExprType[ExprType]]);
  830.     ttBoolean: Result:= Integer(AsBoolean);
  831.   end;
  832. end;
  833.  
  834. function TExpression.GetAsBoolean: Boolean;
  835. begin
  836.   raise EExpression.CreateFmt('Cannot read %s as boolean',
  837.                                [NExprType[ExprType]])
  838. end;
  839.  
  840. function TExpression.CanReadAs(aExprType: TExprType): Boolean;
  841. begin
  842.   Result:= Ord(ExprType) >= Ord(aExprType)
  843. end;
  844.  
  845. function TStringLiteral.GetAsString: String;
  846. begin
  847.   Result:= FAsString
  848. end;
  849.  
  850. function TStringLiteral.GetExprType: TExprType;
  851. begin
  852.   Result:= ttString
  853. end;
  854.  
  855. constructor TStringLiteral.Create( aAsString: String);
  856. begin
  857.   inherited Create;
  858.   FAsString:= aAsString
  859. end;
  860.  
  861. function TFloatLiteral.GetAsString: String;
  862. begin
  863.   Result:= FloatToStr(FAsFloat)
  864. end;
  865.  
  866. function TFloatLiteral.GetAsFloat: Double;
  867. begin
  868.   Result:= FAsFloat
  869. end;
  870.  
  871. function TFloatLiteral.GetExprType: TExprType;
  872. begin
  873.   Result:= ttFloat
  874. end;
  875.  
  876. constructor TFloatLiteral.Create( aAsFloat: Double);
  877. begin
  878.   inherited Create;
  879.   FAsFloat:= aAsFloat
  880. end;
  881.  
  882. function TIntegerLiteral.GetAsString: String;
  883. begin
  884.   Result:= FloatToStr(FAsInteger)
  885. end;
  886.  
  887. function TIntegerLiteral.GetAsFloat: Double;
  888. begin
  889.   Result:= FAsInteger
  890. end;
  891.  
  892. function TIntegerLiteral.GetAsInteger: Integer;
  893. begin
  894.   Result:= FAsInteger
  895. end;
  896.  
  897. function TIntegerLiteral.GetExprType: TExprType;
  898. begin
  899.   Result:= ttInteger
  900. end;
  901.  
  902. constructor TIntegerLiteral.Create( aAsInteger: Integer);
  903. begin
  904.   inherited Create;
  905.   FAsInteger:= aAsInteger
  906. end;
  907.  
  908. function TBooleanLiteral.GetAsString: String;
  909. begin
  910.   Result:= NBoolean[FAsBoolean]
  911. end;
  912.  
  913. function TBooleanLiteral.GetAsFloat: Double;
  914. begin
  915.   Result:= GetAsInteger
  916. end;
  917.  
  918. function TBooleanLiteral.GetAsInteger: Integer;
  919. begin
  920.   Result:= Integer(FAsBoolean)
  921. end;
  922.  
  923. function TBooleanLiteral.GetAsBoolean: Boolean;
  924. begin
  925.   Result:= FAsBoolean
  926. end;
  927.  
  928. function TBooleanLiteral.GetExprType: TExprType;
  929. begin
  930.   Result:= ttBoolean
  931. end;
  932.  
  933. constructor TBooleanLiteral.Create( aAsBoolean: Boolean);
  934. begin
  935.   inherited Create;
  936.   FAsBoolean:= aAsBoolean
  937. end;
  938.  
  939. function TUnaryOp.GetAsFloat: Double;
  940. begin
  941.   case Operator of
  942.     opMinus: Result:= -Operand.AsFloat;
  943.     opPlus: Result:= Operand.AsFloat;
  944.   else
  945.     Result:= inherited GetAsFloat;
  946.   end
  947. end;
  948.  
  949. function TUnaryOp.GetAsInteger: Integer;
  950. begin
  951.   Result:= 0;
  952.   case Operator of
  953.     opMinus: Result:= -Operand.AsInteger;
  954.     opPlus: Result:= Operand.AsInteger;
  955.     opNot:
  956.     case OperandType of
  957.       ttInteger: Result:= not Operand.AsInteger;
  958.       ttBoolean: Result:= Integer(AsBoolean);
  959.     else
  960.       Internal(6);
  961.     end;
  962.   else
  963.     Result:= inherited GetAsInteger;
  964.   end
  965. end;
  966.  
  967. function TUnaryOp.GetAsBoolean: Boolean;
  968. begin
  969.   case Operator of
  970.     opNot: Result:= not(Operand.AsBoolean)
  971.   else
  972.     Result:= inherited GetAsBoolean;
  973.   end
  974. end;
  975.  
  976. function TUnaryOp.GetExprType: TExprType;
  977. begin
  978.   Result:= ResultType(Operator, OperandType)
  979. end;
  980.  
  981. constructor TUnaryOp.Create( aOperator: TOperator; aOperand: TExpression);
  982. begin
  983.   inherited Create;
  984.   Operand:= aOperand;
  985.   Operator:= aOperator;
  986.   OperandType:= Operand.ExprType;
  987.   if not (Operator in [opNot, opPlus, opMinus]) then
  988.     raise EExpression.CreateFmt('%s is not simple unary operator',
  989.                                 [NOperator[Operator]])
  990. end;
  991.  
  992. destructor TUnaryOp.Destroy;
  993. begin
  994.   Operand.Free;
  995.   inherited Destroy
  996. end;
  997.  
  998. function TBinaryOp.GetAsString: String;
  999. begin
  1000.   Result:= '';
  1001.   case ExprType of
  1002.     ttString:
  1003.       case Operator of
  1004.         opPlus: Result:= Operand1.AsString + Operand2.AsString;
  1005.       else
  1006.         Internal(10);
  1007.       end;
  1008.     ttFloat:
  1009.       Result:= FloatToStr(AsFloat);
  1010.     ttInteger:
  1011.       Result:= IntToStr(AsInteger);
  1012.     ttBoolean:
  1013.       Result:= NBoolean[AsBoolean];
  1014.   end
  1015. end;
  1016.  
  1017. function TBinaryOp.GetAsFloat: Double;
  1018. begin
  1019.   Result:= 0;
  1020.   case ExprType of
  1021.     ttFloat:
  1022.       case Operator of
  1023.         opPlus: Result:= Operand1.AsFloat + Operand2.AsFloat;
  1024.         opMinus: Result:= Operand1.AsFloat - Operand2.AsFloat;
  1025.         opMult: Result:= Operand1.AsFloat * Operand2.AsFloat;
  1026.         opDivide: Result:= Operand1.AsFloat / Operand2.AsFloat;
  1027.       else
  1028.         Internal(11);
  1029.       end;
  1030.     ttInteger:
  1031.         Result:= AsInteger;
  1032.     ttBoolean:
  1033.        Result:= Integer(AsBoolean);
  1034.   end
  1035. end;
  1036.  
  1037.  
  1038. function TBinaryOp.GetAsInteger: Integer;
  1039. begin
  1040.   Result:= 0;
  1041.   case ExprType of
  1042.     ttInteger:
  1043.     case Operator of
  1044.       opPlus: Result:= Operand1.AsInteger + Operand2.AsInteger;
  1045.       opMinus: Result:= Operand1.AsInteger - Operand2.AsInteger;
  1046.       opMult: Result:= Operand1.AsInteger * Operand2.AsInteger;
  1047.       opDiv: Result:= Operand1.AsInteger div Operand2.AsInteger;
  1048.       opMod: Result:= Operand1.AsInteger mod Operand2.AsInteger;
  1049.       opShl: Result:= Operand1.AsInteger shl Operand2.AsInteger;
  1050.       opShr: Result:= Operand1.AsInteger shr Operand2.AsInteger;
  1051.       opAnd: Result:= Operand1.AsInteger and Operand2.AsInteger;
  1052.       opOr: Result:= Operand1.AsInteger or Operand2.AsInteger;
  1053.       opXor: Result:= Operand1.AsInteger xor Operand2.AsInteger;
  1054.     else
  1055.       Internal(12);
  1056.     end;
  1057.     ttBoolean:
  1058.       Result:= Integer(GetAsBoolean);
  1059.   end
  1060. end;
  1061.  
  1062. function TBinaryOp.GetAsBoolean: Boolean;
  1063. begin
  1064.   Result:= false;
  1065.   case Operator of
  1066.     opAnd: Result:= Operand1.AsBoolean and Operand2.AsBoolean;
  1067.     opOr: Result:= Operand1.AsBoolean or Operand2.AsBoolean;
  1068.     opXor: Result:= Operand1.AsBoolean xor Operand2.AsBoolean;
  1069.   else
  1070.     Internal(13);
  1071.   end
  1072. end;
  1073.  
  1074. function TBinaryOp.GetExprType: TExprType;
  1075. begin
  1076.   GetExprType:= ResultType(Operator, OperandType)
  1077. end;
  1078.  
  1079. constructor TBinaryOp.Create( aOperator: TOperator; aOperand1, aOperand2: TExpression);
  1080. begin
  1081.   inherited Create;
  1082.   Operator:= aOperator;
  1083.   Operand1:= aOperand1;
  1084.   Operand2:= aOperand2;
  1085.   OperandType:= CommonType(Operand1.ExprType, Operand2.ExprType);
  1086.   if not (Operator in [opMult..opXor]) then
  1087.     raise EExpression.CreateFmt('%s is not a simple binary operator',
  1088.               [NOperator[Operator]])
  1089. end;
  1090.  
  1091. destructor TBinaryOp.Destroy;
  1092. begin
  1093.   Operand1.Free;
  1094.   Operand2.Free;
  1095.   inherited Destroy
  1096. end;
  1097.  
  1098. function TRelationalOp.GetAsString: String;
  1099. begin
  1100.   Result:= NBoolean[AsBoolean]
  1101. end;
  1102.  
  1103. function TRelationalOp.GetAsFloat: Double;
  1104. begin
  1105.   Result:= Integer(AsBoolean)
  1106. end;
  1107.  
  1108. function TRelationalOp.GetAsInteger: Integer;
  1109. begin
  1110.   Result:= Integer(AsBoolean)
  1111. end;
  1112.  
  1113. function TRelationalOp.GetAsBoolean: Boolean;
  1114. begin
  1115.   Result:= false;
  1116.   case CommonType(Operand1.ExprType, Operand2.ExprType) of
  1117.     ttBoolean:
  1118.     case Operator of
  1119.       opEQ: Result:= Operand1.AsBoolean = Operand2.AsBoolean;
  1120.       opNEQ: Result:= Operand1.AsBoolean <> Operand2.AsBoolean;
  1121.     else
  1122.       raise EExpression.CreateFmt('cannot apply %s to boolean operands',
  1123.                                   [NOperator[Operator]]);
  1124.     end;
  1125.  
  1126.     ttInteger:
  1127.     case Operator of
  1128.       opLT: Result:= Operand1.AsInteger < Operand2.AsInteger;
  1129.       opLTE: Result:= Operand1.AsInteger <= Operand2.AsInteger;
  1130.       opGT: Result:= Operand1.AsInteger > Operand2.AsInteger;
  1131.       opGTE: Result:= Operand1.AsInteger >= Operand2.AsInteger;
  1132.       opEQ: Result:= Operand1.AsInteger = Operand2.AsInteger;
  1133.       opNEQ: Result:= Operand1.AsInteger <> Operand2.AsInteger;
  1134.     end;
  1135.  
  1136.     ttFloat:
  1137.     case Operator of
  1138.       opLT: Result:= Operand1.AsFloat < Operand2.AsFloat;
  1139.       opLTE: Result:= Operand1.AsFloat <= Operand2.AsFloat;
  1140.       opGT: Result:= Operand1.AsFloat > Operand2.AsFloat;
  1141.       opGTE: Result:= Operand1.AsFloat >= Operand2.AsFloat;
  1142.       opEQ: Result:= Operand1.AsFloat = Operand2.AsFloat;
  1143.       opNEQ: Result:= Operand1.AsFloat <> Operand2.AsFloat;
  1144.     end;
  1145.  
  1146.     ttString:
  1147.     case Operator of
  1148.       opLT: Result:= Operand1.AsString < Operand2.AsString;
  1149.       opLTE: Result:= Operand1.AsString <= Operand2.AsString;
  1150.       opGT: Result:= Operand1.AsString > Operand2.AsString;
  1151.       opGTE: Result:= Operand1.AsString >= Operand2.AsString;
  1152.       opEQ: Result:= Operand1.AsString = Operand2.AsString;
  1153.       opNEQ: Result:= Operand1.AsString <> Operand2.AsString;
  1154.     end;
  1155.   end
  1156. end;
  1157.  
  1158. function TRelationalOp.GetExprType: TExprType;
  1159. begin
  1160.   Result:= ttBoolean
  1161. end;
  1162.  
  1163. constructor TRelationalOp.Create( aOperator: TOperator; aOperand1, aOperand2: TExpression);
  1164. begin
  1165.   inherited Create;
  1166.   Operator:= aOperator;
  1167.   Operand1:= aOperand1;
  1168.   Operand2:= aOperand2;
  1169.   if not (Operator in RelationalOperators) then
  1170.     raise EExpression.CreateFmt('%s is not relational operator',
  1171.                                  [NOperator[Operator]])
  1172. end;
  1173.  
  1174. destructor TRelationalOp.Destroy;
  1175. begin
  1176.   Operand1.Free;
  1177.   Operand2.Free;
  1178.   inherited Destroy
  1179. end;
  1180.  
  1181. function TParameterList.GetAsString(i: Integer): String;
  1182. begin
  1183.   Result:= Param[i].AsString
  1184. end;
  1185.  
  1186. function TParameterList.GetAsFloat(i: Integer): Double;
  1187. begin
  1188.   Result:= Param[i].AsFloat
  1189. end;
  1190.  
  1191. function TParameterList.GetAsInteger(i: Integer): Integer;
  1192. begin
  1193.   Result:= Param[i].AsInteger
  1194. end;
  1195.  
  1196. function TParameterList.GetAsBoolean(i: Integer): Boolean;
  1197. begin
  1198.   Result:= Param[i].AsBoolean
  1199. end;
  1200.  
  1201. function TParameterList.GetExprType(i: Integer): TExprType;
  1202. begin
  1203.   Result:= Param[i].ExprType
  1204. end;
  1205.  
  1206. function TParameterList.GetParam(i: Integer): TExpression;
  1207. begin
  1208.   Result:= TExpression(Items[i])
  1209. end;
  1210.  
  1211. destructor TParameterList.Destroy;
  1212. var
  1213.   i: Integer;
  1214. begin
  1215.   for i:= 0 to (Count - 1) do
  1216.     TObject(Items[i]).Free;
  1217.   inherited Destroy
  1218. end;
  1219.  
  1220. function TFunction.GetParam(n: Integer): TExpression;
  1221. begin
  1222.   Result:= FParameterList.Param[n]
  1223. end;
  1224.  
  1225. function TFunction.ParameterCount: Integer;
  1226. begin
  1227.   if Assigned(FParameterList) then
  1228.     ParameterCount:= FParameterList.Count
  1229.   else
  1230.     ParameterCount:= 0
  1231. end;
  1232.  
  1233. constructor TFunction.Create( aParameterList: TParameterList);
  1234. begin
  1235.   inherited Create;
  1236.   FParameterList:= aParameterList
  1237. end;
  1238.  
  1239. destructor TFunction.Destroy;
  1240. begin
  1241.   FParameterList.Free;
  1242.   inherited Destroy
  1243. end;
  1244.  
  1245. type
  1246.   TTypeCast =
  1247.   class(TFunction)
  1248.   private
  1249.     Operator: TExprType;
  1250.   protected
  1251.     function GetAsString: String; override;
  1252.     function GetAsFloat: Double; override;
  1253.     function GetAsInteger: Integer; override;
  1254.     function GetAsBoolean: Boolean; override;
  1255.     function GetExprType: TExprType; override;
  1256.   public
  1257.     constructor Create( aParameterList: TParameterList;
  1258.                         aOperator: TExprType);
  1259.   end;
  1260.  
  1261.   TMF =
  1262.     (mfTrunc, mfRound, mfAbs, mfArcTan, mfCos, mfExp, mfFrac, mfInt,
  1263.      mfLn, mfPi, mfSin, mfSqr, mfSqrt, mfPower);
  1264.  
  1265.   TMathExpression =
  1266.   class(TFunction)
  1267.   private
  1268.     Operator: TMF;
  1269.     procedure CheckParameters;
  1270.   protected
  1271.     function GetAsFloat: Double; override;
  1272.     function GetAsInteger: Integer; override;
  1273.     function GetExprType: TExprType; override;
  1274.   public
  1275.     constructor Create( aParameterList: TParameterList;
  1276.                         aOperator: TMF);
  1277.   end;
  1278.  
  1279.   TSF =
  1280.     (sfUpper, sfLower, sfCopy, sfPos, sfLength);
  1281.  
  1282.   TStringExpression =
  1283.   class(TFunction)
  1284.   private
  1285.     Operator: TSF;
  1286.     procedure CheckParameters;
  1287.   protected
  1288.     function GetAsString: String; override;
  1289.     function GetAsInteger: Integer; override;
  1290.     function GetExprType: TExprType; override;
  1291.   public
  1292.     constructor Create( aParameterList: TParameterList;
  1293.                         aOperator: TSF);
  1294.   end;
  1295.  
  1296.  
  1297.   TConditional =
  1298.   class(TFunction)
  1299.   private
  1300.     procedure CheckParameters;
  1301.     function Rex: TExpression;
  1302.   protected
  1303.     function GetAsString: String; override;
  1304.     function GetAsFloat: Double; override;
  1305.     function GetAsInteger: Integer; override;
  1306.     function GetAsBoolean: Boolean; override;
  1307.     function GetExprType: TExprType; override;
  1308.   public
  1309.   end;
  1310.  
  1311. const
  1312.   NTypeCast: array[TExprType] of PChar =
  1313.     ('STRING', 'FLOAT', 'INTEGER', 'BOOLEAN');
  1314.   NMF: array[TMF] of PChar =
  1315.     ('TRUNC', 'ROUND', 'ABS', 'ARCTAN', 'COS', 'EXP', 'FRAC', 'INT',
  1316.      'LN', 'PI', 'SIN', 'SQR', 'SQRT', 'POWER');
  1317.   NSF: array[TSF] of PChar = ('UPPER', 'LOWER', 'COPY', 'POS', 'LENGTH');
  1318.  
  1319. function TStringExpression.GetAsString: String;
  1320. begin
  1321.   CheckParameters;
  1322.   case Operator of
  1323.     sfUpper: Result:= UpperCase(Param[0].AsString);
  1324.     sfLower: Result:= LowerCase(Param[0].AsString);
  1325.     sfCopy: Result:=  Copy(Param[0].AsString, Param[1].AsInteger, Param[2].AsInteger);
  1326.   else
  1327.     Result:= inherited GetAsString;
  1328.   end
  1329. end;
  1330.  
  1331. function TStringExpression.GetAsInteger: Integer;
  1332. begin
  1333.   CheckParameters;
  1334.   case Operator of
  1335.     sfPos: Result:= Pos(Param[0].AsString, Param[1].AsString);
  1336.     sfLength: Result:= Length(Param[0].AsString);
  1337.   else
  1338.     Result:= inherited GetAsInteger
  1339.   end
  1340. end;
  1341.  
  1342. function TStringExpression.GetExprType: TExprType;
  1343. begin
  1344.   case Operator of
  1345.     sfUpper, sfLower, sfCopy: Result:= ttString;
  1346.   else
  1347.     Result:= ttInteger;
  1348.   end
  1349. end;
  1350.  
  1351. procedure TStringExpression.CheckParameters;
  1352. var
  1353.   OK: Boolean;
  1354. begin
  1355.   OK:= false;
  1356.   case Operator of
  1357.     sfUpper, sfLower, sfLength:
  1358.       OK:= (ParameterCount = 1) and
  1359.            (Param[0].ExprType >= ttString);
  1360.     sfCopy:
  1361.       OK:= (ParameterCount = 3) and
  1362.            (Param[0].ExprType >= ttString) and
  1363.            (Param[1].ExprType >= ttInteger) and
  1364.            (Param[2].ExprType >= ttInteger);
  1365.     sfPos:
  1366.       OK:= (ParameterCount = 2) and
  1367.            (Param[0].ExprType >= ttString) and
  1368.            (Param[1].ExprType >= ttString);
  1369.   end;
  1370.   if not OK then
  1371.     raise EExpression.CreateFmt('Invalid parameter to %s',
  1372.                                 [NSF[Operator]])
  1373. end;
  1374.  
  1375. constructor TStringExpression.Create( aParameterList: TParameterList;
  1376.                                       aOperator: TSF);
  1377. begin
  1378.   inherited Create(aParameterList);
  1379.   Operator:= aOperator
  1380. end;
  1381.  
  1382. function TMathExpression.GetAsFloat: Double;
  1383. begin
  1384.   CheckParameters;
  1385.   case Operator of
  1386.     mfAbs: Result:= Abs(Param[0].AsFloat);
  1387.     mfArcTan: Result:= ArcTan(Param[0].AsFloat);
  1388.     mfCos: Result:= Cos(Param[0].AsFloat);
  1389.     mfExp: Result:= Exp(Param[0].AsFloat);
  1390.     mfFrac: Result:= Frac(Param[0].AsFloat);
  1391.     mfInt: Result:= Int(Param[0].AsFloat);
  1392.     mfLn: Result:= Ln(Param[0].AsFloat);
  1393.     mfPi: Result:= Pi;
  1394.     mfSin: Result:= Sin(Param[0].AsFloat);
  1395.     mfSqr: Result:= Sqr(Param[0].AsFloat);
  1396.     mfSqrt: Result:= Sqrt(Param[0].AsFloat);
  1397.     mfPower: Result:=  Exp(Param[1].AsFloat * Ln(Param[0].AsFloat))
  1398.   else
  1399.     Result:= inherited GetAsFloat;
  1400.   end
  1401. end;
  1402.  
  1403. function TMathExpression.GetAsInteger: Integer;
  1404. begin
  1405.   CheckParameters;
  1406.   case Operator of
  1407.     mfTrunc: Result:= Trunc(Param[0].AsFloat);
  1408.     mfRound: Result:= Round(Param[0].AsFloat);
  1409.     mfAbs: Result:= Abs(Param[0].AsInteger);
  1410.   else
  1411.     Result:= inherited GetAsInteger;
  1412.   end
  1413. end;
  1414.  
  1415. procedure TMathExpression.CheckParameters;
  1416. var
  1417.   OK: Boolean;
  1418. begin
  1419.   OK:= True;
  1420.   case Operator of
  1421.     mfTrunc, mfRound, mfArcTan, mfCos, mfExp, mfFrac, mfInt,
  1422.     mfLn, mfSin, mfSqr, mfSqrt, mfAbs:
  1423.     begin
  1424.       OK:= (ParameterCount = 1) and
  1425.            (Param[0].ExprType >= ttFloat);
  1426.     end;
  1427.     mfPower:
  1428.     begin
  1429.       OK:= (ParameterCount = 2) and
  1430.            (Param[0].ExprType >= ttFloat) and
  1431.            (Param[1].ExprType >= ttFloat);
  1432.     end;
  1433.   end;
  1434.   if not OK then
  1435.     raise EExpression.CreateFmt('Invalid parameter to %s',
  1436.                                 [NMF[Operator]])
  1437. end;
  1438.  
  1439. function TMathExpression.GetExprType: TExprType;
  1440. begin
  1441.   case Operator of
  1442.     mfTrunc, mfRound: Result:= ttInteger;
  1443.   else
  1444.     Result:= ttFloat;
  1445.   end
  1446. end;
  1447.  
  1448. constructor TMathExpression.Create( aParameterList: TParameterList;
  1449.                                     aOperator: TMF);
  1450. begin
  1451.   inherited Create(aParameterList);
  1452.   Operator:= aOperator
  1453. end;
  1454.  
  1455.  
  1456. function TTypeCast.GetAsString: String;
  1457. begin
  1458.   Result:= Param[0].AsString
  1459. end;
  1460.  
  1461. function TTypeCast.GetAsFloat: Double;
  1462. begin
  1463.   Result:= Param[0].AsFloat
  1464. end;
  1465.  
  1466. function TTypeCast.GetAsInteger: Integer;
  1467. begin
  1468.   Result:= Param[0].AsInteger
  1469. end;
  1470.  
  1471. function TTypeCast.GetAsBoolean: Boolean;
  1472. begin
  1473.   Result:= Param[0].AsBoolean
  1474. end;
  1475.  
  1476. function TTypeCast.GetExprType: TExprType;
  1477. begin
  1478.   Result:= Operator
  1479. end;
  1480.  
  1481. constructor TTypeCast.Create( aParameterList: TParameterList;
  1482.                               aOperator: TExprType);
  1483. begin
  1484.   inherited Create(aParameterList);
  1485.   Operator:= aOperator
  1486. end;
  1487.  
  1488. function TConditional.Rex: TExpression;
  1489. begin
  1490.   CheckParameters;
  1491.   if Param[0].AsBoolean then
  1492.     Result:= Param[1] else
  1493.     Result:= Param[2]
  1494. end;
  1495.  
  1496.  
  1497. procedure TConditional.CheckParameters;
  1498. begin
  1499.   if not ((ParameterCount = 3) and
  1500.           (Param[0].ExprType = ttBoolean)) then
  1501.     raise EExpression.Create('Invalid parameters to If')
  1502. end;
  1503.  
  1504. function TConditional.GetAsString: String;
  1505. begin
  1506.   Result:= Rex.AsString
  1507. end;
  1508.  
  1509. function TConditional.GetAsFloat: Double;
  1510. begin
  1511.   Result:= Rex.AsFloat
  1512. end;
  1513. function TConditional.GetAsInteger: Integer;
  1514. begin
  1515.   Result:= Rex.AsInteger
  1516. end;
  1517. function TConditional.GetAsBoolean: Boolean;
  1518. begin
  1519.   Result:= Rex.AsBoolean
  1520. end;
  1521. function TConditional.GetExprType: TExprType;
  1522. begin
  1523.   Result:= Rex.ExprType
  1524. end;
  1525.  
  1526. function StandardFunctions (const Ident: String; PL: TParameterList): TExpression;
  1527. var
  1528.   i: TExprType;
  1529.   j: TMF;
  1530.   k: TSF;
  1531.   Found: Boolean;
  1532. begin
  1533.   Found:= false;
  1534.   if Ident = 'IF' then
  1535.   begin
  1536.     Result:= TConditional.Create(PL)
  1537.   end else
  1538.   begin
  1539.     for i:= Low(TExprType) to High(TExprType) do
  1540.     begin
  1541.       if Ident = NTypeCast[i] then
  1542.       begin
  1543.         Found:= true;
  1544.         Break
  1545.       end;
  1546.     end;
  1547.     if Found then
  1548.     begin
  1549.       Result:= TTypeCast.Create(PL, i)
  1550.     end else
  1551.     begin
  1552.       for j:= Low(TMF) to High(TMF) do
  1553.       begin
  1554.         if Ident = NMF[j] then
  1555.         begin
  1556.           Found:= true;
  1557.           break
  1558.         end
  1559.       end;
  1560.       if Found then
  1561.       begin
  1562.         Result:= TMathExpression.Create(PL, j)
  1563.       end else
  1564.       begin
  1565.         for k:= Low(TSF) to High(TSF) do
  1566.         begin
  1567.           if Ident = NSF[k] then
  1568.           begin
  1569.             Found:= true;
  1570.             break
  1571.           end
  1572.         end;
  1573.         if Found then
  1574.         begin
  1575.           Result:= TStringExpression.Create(PL, k)
  1576.         end else
  1577.         begin
  1578.           Result:= nil
  1579.         end
  1580.       end
  1581.     end
  1582.   end
  1583. end;
  1584.  
  1585. {parser...}
  1586. const
  1587.   OpTokens: array[TOperator] of PChar =
  1588.               ( 'NOT',
  1589.                 '*', '/', 'DIV', 'MOD', 'AND', 'SHL', 'SHR',
  1590.                 '+', '-', 'OR', 'XOR',
  1591.                 '=', '<>', '<', '>', '<=', '>=');
  1592. const
  1593.   Whitespace = [#$1..#$20];
  1594.   Digits = ['0'..'9'];
  1595.   SignChars = ['+', '-'];
  1596.   RelationalChars = ['<', '>', '='];
  1597.   OpChars = SignChars + ['/', '*'] + RelationalChars;
  1598.  
  1599.   OpenSub = '(';
  1600.   CloseSub = ')';
  1601.   SQuote = '''';
  1602.   PrimaryIdentChars = ['a'..'z', 'A'..'Z', '_'];
  1603.   IdentChars = PrimaryIdentChars + Digits;
  1604.  
  1605. function CreateExpression( const S: String;
  1606.                 IdentifierFunction: TIdentifierFunction): TExpression;
  1607.  
  1608. var
  1609.   P: PChar;
  1610.  
  1611. function Expression: TExpression;
  1612.  
  1613. procedure SwallowWhitespace;
  1614. begin
  1615.   while P^ in Whitespace do inc(P)
  1616. end;
  1617.  
  1618. function EoE: Boolean;
  1619. begin
  1620.   Result:= (P^ = #0) or (P^ = CloseSub) or (P^ = ',')
  1621. end;
  1622.  
  1623. function UnsignedFloat: TExpression;
  1624. type
  1625.   TNScan = (nsMantissa, nsDPFound, nsExpFound, nsFound);
  1626. var
  1627.   S: String[30];
  1628.   State: TNScan;
  1629.   Int: Boolean;
  1630.  
  1631. procedure Bomb;
  1632. begin
  1633.   raise EExpression.Create('Bad numeric format')
  1634. end;
  1635.  
  1636. begin
  1637.   S:= '';
  1638.   Int:= false;
  1639.   State:= nsMantissa;
  1640.   repeat
  1641.     if P^ in Digits then
  1642.     begin
  1643.       S:= S + P^;
  1644.       inc(P)
  1645.     end else
  1646.     if P^ = '.' then
  1647.     begin
  1648.       if State = nsMantissa then
  1649.       begin
  1650.         S:= S + P^;
  1651.         inc(P);
  1652.         State:= nsDPFound
  1653.       end else
  1654.       begin
  1655.         Bomb
  1656.       end;
  1657.     end else
  1658.     if (P^ = 'e') or (P^ = 'E') then
  1659.     begin
  1660.       if (State = nsMantissa) or
  1661.          (State = nsDPFound) then
  1662.       begin
  1663.         S:= S + 'E';
  1664.         inc(P);
  1665.         if P^ = '-' then
  1666.         begin
  1667.           S:= S + P^;
  1668.           inc(P);
  1669.         end;
  1670.         State:= nsExpFound;
  1671.         if not (P^ in Digits) then
  1672.           Bomb
  1673.       end else
  1674.       begin
  1675.         Bomb
  1676.       end
  1677.     end else
  1678.     begin
  1679.       Int:= (State = nsMantissa);
  1680.       State:= nsFound
  1681.     end;
  1682.     if Length(S) > 28 then
  1683.       Bomb
  1684.   until State = nsFound;
  1685.   if Int then
  1686.     Result:= TIntegerLiteral.Create(StrToInt(S))
  1687.   else
  1688.     Result:= TFloatLiteral.Create(StrToFloat(S))
  1689. end;
  1690.  
  1691. function CharacterString: TExpression;
  1692. var
  1693.   SR: String;
  1694. begin
  1695.   SR:= '';
  1696.   repeat
  1697.     inc(P);
  1698.     if P^ = SQuote then
  1699.     begin
  1700.      inc(P);
  1701.       if P^ <> SQuote then
  1702.         break;
  1703.      end;
  1704.      if P^ = #0 then
  1705.        raise EExpression.Create('Unterminated string');
  1706.      if Length(SR) > MaxStringLength then
  1707.        raise EExpression.Create('String too long');
  1708.      SR:= SR + P^;
  1709.   until false;
  1710.   Result:= TStringLiteral.Create(SR)
  1711. end;
  1712.  
  1713. type
  1714.   TTokType = (ttIdentifier, ttOperator, ttBooleanLiteral);
  1715.  
  1716. function GetTok( var Ident: String;
  1717.                  var Operator: TOperator;
  1718.                  var BoolLit: Boolean): TTokType;
  1719. var
  1720.   Found: Boolean;
  1721.   LocalOp: TOperator;
  1722. begin
  1723.   Found:= false;
  1724.   Ident:= UpCase(P^);
  1725.   Result:= ttIdentifier;
  1726.   repeat
  1727.     inc(P);
  1728.     if P^ in IdentChars then
  1729.       Ident:= Ident + UpCase(P^)
  1730.     else
  1731.       Found:= true
  1732.   until Found;
  1733.  
  1734.   for LocalOp:= Low(TOperator) to High(TOperator) do
  1735.   begin
  1736.     if OpTokens[LocalOp] = Ident then
  1737.     begin
  1738.       Result:= ttOperator;
  1739.       Operator:= LocalOp;
  1740.       break
  1741.     end;
  1742.   end;
  1743.  
  1744.   if Result = ttIdentifier then
  1745.   begin
  1746.     if Ident = 'TRUE' then
  1747.     begin
  1748.       Result:= ttBooleanLiteral;
  1749.       BoolLit:= true
  1750.     end else
  1751.     if Ident = 'FALSE' then
  1752.     begin
  1753.       Result:= ttBooleanLiteral;
  1754.       BoolLit:= false
  1755.     end
  1756.   end
  1757. end;
  1758.  
  1759. function Factor: TExpression;
  1760. var
  1761. {from GetTok}
  1762.   Identifier: String;
  1763.   Operator: TOperator;
  1764.   BoolLit: Boolean;
  1765.   PList: TParameterList;
  1766.   MoreParameters: Boolean;
  1767. begin {factor}
  1768.   Result:= nil;
  1769.   try
  1770.     SwallowWhitespace;
  1771.     if P^ in SignChars then
  1772.     begin
  1773.       case P^ of
  1774.         '+':
  1775.         begin
  1776.           Inc(P);
  1777.           Result:= TUnaryOp.Create(opPlus, Factor);
  1778.         end;
  1779.         '-':
  1780.         begin
  1781.           Inc(P);
  1782.           Result:= TUnaryOp.Create(opMinus, Factor);
  1783.         end;
  1784.       end
  1785.     end else
  1786.     if P^ = SQuote then
  1787.     begin
  1788.       Result:= CharacterString;
  1789.     end else
  1790.     if P^ in Digits then
  1791.     begin
  1792.       Result:= UnsignedFloat;
  1793.     end else
  1794.     if P^ = OpenSub then
  1795.     begin
  1796.       Inc(P);
  1797.       Result:= Expression;
  1798.       if P^ = CloseSub then
  1799.         inc(P)
  1800.       else
  1801.         raise EExpression.Create(' ) expected')
  1802.     end else
  1803.     if P^ in PrimaryIdentChars then
  1804.     begin
  1805.       case GetTok(Identifier, Operator, BoolLit) of
  1806.         ttOperator:
  1807.         if Operator = opNot then
  1808.         begin
  1809.           inc(P);
  1810.           Result:= TUnaryOp.Create(opNot, Factor)
  1811.         end else
  1812.         begin
  1813.           raise EExpression.CreateFmt('%s not allowed here', [NOperator[Operator]]);
  1814.         end;
  1815.         ttIdentifier:
  1816.         begin
  1817.           PList:= nil;
  1818.           try
  1819.             SwallowWhitespace;
  1820.             MoreParameters:= P^ = OpenSub;
  1821.             if MoreParameters then
  1822.             begin
  1823.               PList:= TParameterList.Create;
  1824.               while MoreParameters do
  1825.               begin
  1826.                 inc(P);
  1827.                 PList.Add(Expression);
  1828.                 MoreParameters:= P^ = ','
  1829.               end;
  1830.               {bug fix 11/11/97}
  1831.               if P^ = CloseSub then
  1832.                 Inc(P)
  1833.               else
  1834.                 raise EExpression.Create('Incorrectly formed parameters')
  1835.             end;
  1836.             Result:= StandardFunctions(Identifier, PList);
  1837.             if (Result = nil) and Assigned(IdentifierFunction) then
  1838.               Result:= IdentifierFunction(Identifier, PList);
  1839.             if Result = nil then
  1840.               raise EExpression.CreateFmt('Unknown Identifier %s', [Identifier]);
  1841.           finally
  1842.             if Result = nil then
  1843.               PList.Free
  1844.           end
  1845.         end;
  1846.         ttBooleanLiteral:
  1847.         begin
  1848.           Result:= TBooleanLiteral.Create(BoolLit)
  1849.         end
  1850.       end;
  1851.     end else
  1852.     if EoE then
  1853.     begin
  1854.       raise EExpression.Create('Unexpected end of factor')
  1855.     end else
  1856.     begin
  1857.       raise EExpression.Create('Syntax error') {leak here ?}
  1858.     end
  1859.   except
  1860.     Result.Free;
  1861.     raise
  1862.   end
  1863. end;  {factor}
  1864.  
  1865. function Term: TExpression;
  1866. var
  1867.   Identifier: String;
  1868.   Operator: TOperator;
  1869.   BoolLit: Boolean;
  1870.   SavedP: PChar;
  1871.  
  1872. begin {term}
  1873.   Result:= Factor;
  1874.   try
  1875.     SwallowWhitespace;
  1876.     if EoE then
  1877.     begin
  1878.     end else
  1879.     if (P^ = '*') then
  1880.     begin
  1881.       inc(P);
  1882.       Result:= TBinaryOp.Create(opMult, Result, Term)
  1883.     end else
  1884.     if (P^ = '/') then
  1885.     begin
  1886.       inc(P);
  1887.       Result:= TBinaryOp.Create(opDivide, Result, Term);
  1888.     end else
  1889.     if P^ in OpChars then  {only checks for single char operators}
  1890.     begin
  1891.     end else
  1892.     if P^ in PrimaryIdentChars then
  1893.     begin
  1894.       SavedP:= P;
  1895.       case GetTok(Identifier, Operator, BoolLit) of
  1896.         ttIdentifier:
  1897.         begin
  1898.           raise EExpression.CreateFmt('Identifier %s not allowed here', [Identifier]);
  1899.         end;
  1900.         ttOperator:
  1901.         if Operator in [opAnd, opDiv, opMod, opShl, opShr] then
  1902.         begin
  1903.           Result:= TBinaryOp.Create(Operator, Result, Term);
  1904.         end else
  1905.         begin
  1906.           P:= SavedP; {push token back - not ours}
  1907.         end;
  1908.         ttBooleanLiteral:
  1909.         begin
  1910.           raise EExpression.Create('Boolean literal not allowed here')
  1911.         end
  1912.       end
  1913.     end else
  1914.     begin
  1915.       raise EExpression.CreateFmt('char %s in input stream', [P^]);
  1916.     end
  1917.   except
  1918.     Result.Free;
  1919.     raise
  1920.   end
  1921. end;  {term}
  1922.  
  1923. function Simple: TExpression;
  1924. var
  1925.   Identifier: String;
  1926.   Operator: TOperator;
  1927.   BoolLit: Boolean;
  1928.   SavedP: PChar;
  1929. begin {simple}
  1930.   Result:= Term;
  1931.   try
  1932.     SwallowWhitespace;
  1933.     if EoE then
  1934.     begin  {finished}
  1935.     end else
  1936.     if (P^ = '+') then
  1937.     begin
  1938.       inc(P);
  1939.       Result:= TBinaryOp.Create(opPlus, Result, Simple)
  1940.     end else
  1941.     if (P^ = '-') then
  1942.     begin
  1943.       inc(P);
  1944.       Result:= TBinaryOp.Create(opMinus, Result, Simple)
  1945.     end else
  1946.     if P^ in OpChars then  {only checks for single char operators}
  1947.     begin    {finished}
  1948.     end else
  1949.     begin
  1950.       SavedP:= P;
  1951.       case GetTok(Identifier, Operator, BoolLit) of
  1952.         ttIdentifier:
  1953.         begin
  1954.           raise EExpression.CreateFmt('Identifier %s not allowed here', [Identifier])
  1955.         end;
  1956.         ttOperator:
  1957.         if (Operator = opOr) or (Operator = opXor) then
  1958.           Result:= TBinaryOp.Create(Operator, Result, Term)
  1959.         else
  1960.         begin
  1961.           P:= SavedP  {push token back - not ours}
  1962.         end;
  1963.         ttBooleanLiteral:
  1964.         begin
  1965.           raise EExpression.Create('Boolean literal not allowed here')
  1966.         end
  1967.       end
  1968.     end
  1969.   except
  1970.     Result.Free;
  1971.     raise
  1972.   end
  1973. end;  {simple}
  1974.  
  1975. var
  1976.   OpString: String;
  1977.   Op: TOperator;
  1978.   OpFound: Boolean;
  1979.   Finished: Boolean;
  1980. begin {expression}
  1981.   Result:= nil;
  1982.   try
  1983.     Finished:= false;
  1984.     repeat
  1985.       SwallowWhitespace;
  1986.       if not EoE then
  1987.       begin
  1988.         Result:= Simple;
  1989.         if P^ in RelationalChars then
  1990.         begin
  1991.           OpString:= P^;
  1992.           inc(P);
  1993.           if P^ in RelationalChars then
  1994.           begin
  1995.             OpString:= OpString + P^;
  1996.             inc(P)
  1997.           end;
  1998.           OpFound:= false;
  1999.           for Op:= opEQ to opGTE do
  2000.           if OpTokens[Op] = OpString then
  2001.           begin
  2002.             OpFound:= true;
  2003.             break
  2004.           end;
  2005.           if not OpFound then
  2006.             raise EExpression.CreateFmt('%s not a valid operator', [OpString])
  2007.           else
  2008.             Result:= TRelationalOp.Create(Op, Result, Simple)
  2009.         end
  2010.       end else
  2011.       begin
  2012.         Finished:= true
  2013.       end
  2014.     until Finished
  2015.   except
  2016.     Result.Free;
  2017.     raise
  2018.   end
  2019. end;  {expression}
  2020.  
  2021. begin
  2022.   P:= PChar(S);
  2023.   Result:= Expression
  2024. {bug - P^ may equal ')' at this stage... &&&}
  2025. end;
  2026.  
  2027. end.
  2028.  
  2029. {
  2030. 18/6/97
  2031. loosely based on syntax diagrams in BP7 Language Guide pages 66 to 79.
  2032. This is where the nomenclature Term, Factor, SimpleExpression, Expression is
  2033. derived.
  2034.  
  2035. written for Mark Page's troxler thing - as part of the report definition language,
  2036. but might be needed for Robot application framework. Not tested much.
  2037.  
  2038.  
  2039. 7/9/97
  2040. function handling completely changed.
  2041.  
  2042. added support for Integers including support for the following operators
  2043.   bitwise not
  2044.   bitwise and
  2045.   bitwise or
  2046.   bitwise xor
  2047.   shl
  2048.   shr
  2049.   div
  2050.  
  2051. now support std functions:
  2052.  
  2053. arithmetic...
  2054.   TRUNC, ROUND, ABS, ARCTAN, COS, EXP, FRAC, INT,
  2055.      LN, PI, SIN, SQR, SQRT, POWER
  2056.  
  2057. string...
  2058.   UPPER, LOWER, COPY, POS, LENGTH
  2059.  
  2060. Fixed a couple of minor bugs. Forgotten what they are.
  2061.  
  2062. 16/9/97
  2063. realised (whilst lying in the bath) that the way this unit
  2064. handles parameters is a bit daft. It should be possible to
  2065. pass the parameter stack to the identifier function. The only
  2066. problem with this approach is how to handle disposal of the stack.
  2067.  
  2068. We could require that the identifier function disposes of the stack...
  2069. I don't really like this (I can't think why at the moment). Another
  2070. approach would be to define a 'placeholder' expression which does nothing
  2071. but hold the parameter list and the <clients> expression.
  2072.  
  2073. Compromise solution:
  2074.   The parser constructs an instance of TParameter list and passes it to
  2075.   the 'user' via a call to IdentifierFunction. There are four possible
  2076.   mechanisms for disposal of the parameter list.
  2077.      a) If the Identifier function returns NIL the parser disposes
  2078.         of the parameter list then raises 'Unknown identifier'.
  2079.      b) If the Identifier function raises an exception then the parser
  2080.         catches this exception (in a 'finally' clause) and disposes
  2081.         of the parameter list.
  2082.      c) If the Identifier function returns an expression then it must
  2083.         dispose of the parameter list if it does not wish to keep it.
  2084.      d) If the Identifier function returns an expression which is
  2085.         derived from TFunction, then it may pass the parameter list to
  2086.         its result. The result frees the parameter list when it is freed.
  2087.         (i.e. ParameterList passed to TFunction.Create is freed by
  2088.         TFunction.Destroy)
  2089.  
  2090.  
  2091. Simple rule - if IdentFunction returns Non-nil then parameters are
  2092. responsiblity of the object returned. Otherwise caller will handle. OK?
  2093.  
  2094. 5/11/97
  2095. First issue of Troxler.exe
  2096.  
  2097. 11/11/97
  2098. Bug caused mishandling of function lists. Fixed.
  2099.  
  2100. 30/12/97
  2101. Some slight restructing. Added more comprehensive documentation. Removed
  2102. a few calls to StrPas which are redundant under D2/D3
  2103.  
  2104.  
  2105. General Comments/Notes
  2106. ----------------------
  2107.  
  2108. String is superset of Float is superset of Integer is Superset of Boolean
  2109. this is not quite like pascal...
  2110.  
  2111. String - Float - Integer - Boolean
  2112. <-upcast                 downcast->
  2113.  
  2114. Boolean can always be read as Integer (True = 1 false = 0)
  2115. But integer can never be read as Boolean.
  2116.  
  2117. Float can always be read as String but string can NEVER be read as Float -
  2118. even if string forms valid Float.
  2119.  
  2120. Often explicit casts are not required
  2121.  
  2122. Enforcement of type compatibility is a great deal less strict than Pascal.
  2123.  
  2124. If an operator requires a particular type of operand then both operands
  2125. are upcast to the nearest compatible type.
  2126.  
  2127. I have arbitrarily asserted that both parties to a relational operator must
  2128. be of identical (not compatible) type. This may be a bad decision and
  2129. perhaps implicit Upcasts (like that above) should be allowed. Not difficult
  2130. to do... Can always use specific upcast. I think a downcast always fails.
  2131.  
  2132. Client defined identifiers sort of supported.
  2133. }
  2134.  
  2135.  
  2136.